#include <iostream>
#include <cstring>
#include <fstream>
#include <vector>
#include <map>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <math.h>

extern "C"
{
#include "cdf.h"
#include "cdf.c"
}

// Port from Traffic Generator
// Acknowledged to https://github.com/HKUST-SING/TrafficGenerator/blob/master/src/common/common.c
double poission_gen_interval(double avg_rate)
{
  if (avg_rate > 0)
     return -logf(1.0 - (double)rand() / RAND_MAX) / avg_rate;
  else
     return 0;
}

template<typename T>
T rand_range (T min, T max)
{
  return min + ((double)max - min) * rand () / RAND_MAX;
}

using namespace std;
int main(int argc, char *argv[])
{
  int numHosts = 4;
	int numLinks = 4;
  int numSwitches = 16;
	double link_sh_Bw = 1e9;
	double link_ss_Bw = 1e9;
  
  double load = 0.4;
  unsigned randomSeed = 1;
  double FLOW_LAUNCH_END_TIME = 1;
  std::string cdfFileName = "DCTCP_CDF.txt";
  std::string flowsFileName = "flows.txt";
  std::string trafficPattern = "random";

  //parameter
  for(int i=0; i<argc; i++)  
  { 
    if (!strcmp(argv[i], "numHosts"))
    {
      numHosts = atoi(argv[i+1]);
      cout<<"numHosts="<<numHosts<<endl;
      i++;
    }
    if (!strcmp(argv[i], "numLinks"))
    {
      numLinks = atoi(argv[i+1]);
      cout<<"numLinks="<<numLinks<<endl;
      i++;
    }
    if (!strcmp(argv[i], "numSwitches"))
    {
      numSwitches = atoi(argv[i+1]);
      cout<<"numSwitches="<<numSwitches<<endl;
      i++;
    }
    if (!strcmp(argv[i], "link_sh_Bw"))
    {
      link_sh_Bw = atof(argv[i+1]);
      cout<<"link_sh_Bw="<<link_sh_Bw<<endl;
      i++;
    }
    if (!strcmp(argv[i], "link_ss_Bw"))
    {
      link_ss_Bw = atof(argv[i+1]);
      cout<<"link_ss_Bw="<<link_ss_Bw<<endl;
      i++;
    }
    if (!strcmp(argv[i], "load"))
    {
      load = atof(argv[i+1]);
      cout<<"load="<<load<<endl;
      i++;
    }
    if (!strcmp(argv[i], "randomSeed"))
    {
      randomSeed = atoi(argv[i+1]);
      cout<<"randomSeed="<<randomSeed<<endl;
      i++;
    }
    if (!strcmp(argv[i], "FLOW_LAUNCH_END_TIME"))
    {
      FLOW_LAUNCH_END_TIME = atof(argv[i+1]);
      cout<<"FLOW_LAUNCH_END_TIME="<<FLOW_LAUNCH_END_TIME<<endl;
      i++;
    }
    if (!strcmp(argv[i], "cdfFileName"))
    {
      cdfFileName = argv[i+1];
      cout<<"cdfFileName="<<cdfFileName<<endl;
      i++;
    }
    if (!strcmp(argv[i], "flowsFileName"))
    {
      flowsFileName = argv[i+1];
      cout<<"flowsFileName="<<flowsFileName<<endl;
      i++;
    }
    if (!strcmp(argv[i], "trafficPattern"))
    {
      trafficPattern = argv[i+1];
      cout<<"trafficPattern="<<trafficPattern<<endl;
      i++;
    }
  }  

  struct cdf_table* cdfTable = new cdf_table ();
  init_cdf (cdfTable);
  load_cdf (cdfTable, cdfFileName.c_str ());
  double oversubRatio = (numHosts * link_sh_Bw) / (numLinks * link_ss_Bw);
  double requestRate = load * link_sh_Bw * numHosts / oversubRatio / (8 * avg_cdf (cdfTable)) / numHosts;
  
  std::cout << "Over-subscription ratio: " << oversubRatio << endl;
  std::cout <<  "load: " << load << endl;
  std::cout <<  "avg_cdf: " << avg_cdf (cdfTable) << endl;
  std::cout <<  "Average request rate: " << requestRate << " per second" << endl;
  std::cout <<  "Initialize random seed: " << randomSeed << endl;
        
  if (randomSeed == 0)
  {
    srand ((unsigned)time (NULL));
  }
  else
  {
    srand (randomSeed);
  }

  long flowCount = 0;
  long totalFlowSize = 0;

  std::ofstream flows;
  flows.open (flowsFileName);
  
  for (uint32_t i = 0; i < numSwitches; i++)
  { 
    for (uint32_t j = 0; j < numHosts; j++)
    {
      double start_time = poission_gen_interval (requestRate);
      while (start_time < FLOW_LAUNCH_END_TIME)
      {
        flowCount ++;
        uint32_t i_remote, j_remote;
        if (trafficPattern == "random")
        {
          while (true){
            i_remote = rand_range (0, numSwitches);
            if (i_remote!=i)
              break;
          }
          j_remote = rand_range (0, numHosts);
        }
        /*else if (trafficPattern == "cluster"){

        }*/
        else if (trafficPattern == "permutation"){
          i_remote = (i + 4) % numSwitches;
          j_remote = j;
        }
        
        uint32_t flowSize = gen_random_cdf (cdfTable);
        totalFlowSize += flowSize;

        uint32_t Host_id = numHosts*i+j+1;
        uint32_t Host_remote_id = numHosts*i_remote+j_remote+1;
        flows << start_time << " " << Host_id << " " << Host_remote_id << " flowSize: " << flowSize << endl;
        start_time += poission_gen_interval (requestRate);
      }
    }
  }
  flows.close();
  std::cout << "flowCount:" << flowCount << endl;
  std::cout << "totalFlowSize:" << totalFlowSize << endl;
	return 0;
}